// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#pragma once

#include <cstdio>
#include <map>
#include <memory>
#include <set>
#include <string>

#include <rapidjson/document.h>

#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/gdax/symbology/currency.h"
#include "coin2/exchange/gdax/symbology/product.h"
namespace coin2::exchange::gdax::order_v1 {

constexpr auto BUY = '1';
constexpr auto SELL = '2';
constexpr auto MSG_TYPE_SESSION_REJECT = "3";
constexpr auto MSG_TYPE_EXECUTION_REPORT = "8";
constexpr auto MSG_TYPE_ORDER_CANCEL_REJECT = "9";
constexpr auto EXEC_TYPE_NEW_ORDER = '0';
constexpr auto EXEC_TYPE_FILL = '1';
constexpr auto EXEC_TYPE_DONE = '3';
constexpr auto EXEC_TYPE_CANCELED = '4';
constexpr auto EXEC_TYPE_STOPPED = '7';
constexpr auto EXEC_TYPE_REJECTED = '8';
constexpr auto EXEC_TYPE_ORDER_CHANGED = 'D';
constexpr auto EXEC_TYPE_ORDER_STATUS = 'I';
constexpr auto ORD_STATUS_NEW_ORDER = '0';
constexpr auto ORD_STATUS_PARTIALLY_FILLED = '1';
constexpr auto ORD_STATUS_FULLY_FILLED = '3';
constexpr auto ORD_STATUS_CANCELLED_ORDER = '4';
constexpr auto ORD_STATUS_RESIZED_ORDER = '5';

class GdaxParser : public base::order::BaseParser {
 public:
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  AccountBalance ParseBalance(const JsonValue& doc) override;
  bool ParseRawBalance(const JsonValue& doc, RawBalanceMap* raw_balance_map);

 private:
  void ParseSubmitResponse();
  void ParseCancelResponse();
  void ParseMultiCancelResponse();
  void ParseRestOrdersInfo();
  void ParseRestFillOrders();
  void ParseRestAccountBalance();
  void ParseRestDeposit();
  void ParseRestWithdrawal();
  void ParseWsMessage();
  void ParseFixMessage();
  void ParseRestTransfers(const std::string& id);
  void ParseRestTransfersInit();

  ProductOrderBundle ParseWsOrderInfoListV1(const JsonValue& data);
  ProductFillBundle ParseWsFillInfoListV1(const JsonValue& data);
  coin::proto::ProductOrderElement ParseRestOrderInfo(const JsonValue& data);
  void ParseFixSubmitResponse(const FIX::Message& message);
  void ParseFixCancelResponse(const FIX::Message& message);
  void ParseFixFillInfo(const FIX::Message& message);
  void ParseFixOrderInfo(const FIX::Message& message);
  void SetProductFillBundleFromRaw(const JsonValue& data, ProductFillBundle* product_fill_bundle);

  template <class T>
  inline std::optional<int64_t> ToInt64(T&& val) const {
    try {
      return stoll(val);
    } catch (std::exception& ex) {
      return {};
    }
  }

 private:
  std::map<std::string, double> order_qty_map_;
  std::map<std::string, coin::proto::OrderType> order_type_map_;
  bool is_account_balance_initialized_ = false;
  std::map<std::string, std::string> transfer_deposit_map_;
  bool is_transfer_deposit_initialized_ = false;
  std::map<std::string, std::string> transfer_withdrawal_map_;
  bool is_transfer_withdrawal_initialized_ = false;
  std::map<std::string, std::string> transfer_subaccount_map_;
  std::set<std::string> is_transfer_subaccount_initialized_set_;

  std::unordered_set<std::string> trade_currency_set_ = {"USD", "USDT", "ETH",   "BTC",  "MANA",
                                                         "ZEC", "BAT",  "CVC",   "BCH",  "LTC",
                                                         "XRP", "ETC",  "LINK",  "EUR",  "YFI",
                                                         "OMG", "DOGE", "1INCH", "SHIB", "FIL",
                                                         "EOS", "ADA"};  // noted!! gdax native_base
};

}  // namespace coin2::exchange::gdax::order_v1
